/*
 * JSFConsole - Lightweight JSF Application Monitor
 * Copyright (C) 2009  Grzegorz Bugaj
 * http://www.gregbugaj.com
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
package com.gregbugaj.jsfconsole.util;

import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import javax.el.ArrayELResolver;
import javax.el.BeanELResolver;
import javax.el.CompositeELResolver;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.ListELResolver;
import javax.el.MapELResolver;
import javax.el.ValueExpression;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import com.gregbugaj.jsfconsole.enums.ScopeType;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.el.ImplicitObjectELResolver;
import com.sun.faces.el.ELUtils.Scope;
import com.sun.faces.mgbean.BeanBuilder;
import com.sun.faces.mgbean.BeanManager;
/**
 * Helper class for working with JSF and Facelets
 * @author gbugaj
 *
 */
public class FacesUtil {
	/**
	 * Called for components that accept f:attribute like commandLink or commandButton
	 * example:
	 *  <h:commandLink value="Click here" actionListener="#{myBean.action}">
	        <f:attribute name="attributeName1" value="attributeValue1" />
	        <f:attribute name="attributeName2" value="attributeValue2" />
    	</h:commandLink>
	 * @param event JSF Event
	 * @param name of the attribute we want to get
	 * @return
	 */
	public static String getActionAttribute(ActionEvent event, String name) {
		Object obj=event.getComponent().getAttributes().get(name);		
		if(obj==null){
			throw new IllegalArgumentException("Attribute '"+name+"' dosen't exist");
		}
		return obj.toString();
	}


	public static Object getValueFromELExpression(String expression){
		FacesContext context=FacesContext.getCurrentInstance();
		ELContext ctx = context.getELContext();
		Application application=context.getApplication();
		ValueExpression expr=application
		.getExpressionFactory()
		.createValueExpression(context.getELContext(), expression, Object.class);
		Object value = expr.getValue(ctx);
		return value;
	}


	/**
	 * Retrieve all registered beans for given {@link ScopeType}
	 * @param scopeType to search in
	 * @return List of beanNames
	 */
	public static List<String> getRegisteredBeans(ScopeType scopeType){
		FacesContext facesContext=FacesContext.getCurrentInstance();
        List<String> registeredBeans=new ArrayList<String>();
		ApplicationAssociate application = ApplicationAssociate.getInstance(facesContext.getExternalContext());
		BeanManager  beanManager = application.getBeanManager();
		Map<String, BeanBuilder> beanMap=beanManager.getRegisteredBeans();
		Set<Entry<String, BeanBuilder>>beanEntries=beanMap.entrySet();
		for(Entry<String, BeanBuilder> bean:beanEntries){
			String beanName=bean.getKey();
			if(!beanManager.isManaged(beanName)){
				continue;
			}			
			BeanBuilder builder=bean.getValue();
			Scope bScope=builder.getScope();
			if(scopeType==ScopeType.ALL || bScope.toString().equals(scopeType.toString())){
				registeredBeans.add(beanName+":"+bScope.toString());
			}
		}

		if(scopeType==ScopeType.ALL || scopeType==ScopeType.IMPLICIT){
			List<String> implicitList=getProperties(new ImplicitObjectELResolver(), null);
			for(String implicitBeanName:implicitList){
				registeredBeans.add(implicitBeanName+":"+ScopeType.IMPLICIT.toString());
			}
		}

		Collections.sort(registeredBeans);
		return registeredBeans;
	}


	public static List<String> getPropertiesForExpression(ELResolver resolver, String expression){
		return getProperties(resolver, FacesUtil.getValueFromELExpression(expression));
	}

	public static List<String> getProperties(ELResolver resolver, Object target){
		FacesContext facesContext=FacesContext.getCurrentInstance();
		Iterator<FeatureDescriptor> descriptor=resolver.getFeatureDescriptors(facesContext.getELContext(), target);
		List<String> properties=Collections.<String>emptyList();	

		String[] filteredProperties=new String[]{"class", "empty", "bytes"};
		if(descriptor!=null){
			/*
			try{
				}

				catch(NullPointerException npe){
					System.out.println("NPE:"+npe.getMessage());
				}
			 */
			properties = new ArrayList<String>();
			while(descriptor.hasNext()){
				FeatureDescriptor des=descriptor.next();				
				String name=des.getDisplayName();	
				boolean isNotFiltered=true;
				for(String prop:filteredProperties){
					if(name.equals(prop)){
						isNotFiltered=false;
						break;
					}
				}
				if(isNotFiltered){
					properties.add(name);			
				}
			}
		}
		Collections.sort(properties);
		return properties;
	}
}
